using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace ZSW.Framework.Utils
{
    public static class ZSWFUtil_Space
    {
        /// <summary>
        /// 设置 Vector2 的 x 值
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Vector2 SetX(Vector2 v, float x)
        {
            Vector2 tmpV = v;
            tmpV.x = x;
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 设置 Vector2 的 y 值
        /// </summary>
        /// <param name="v"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static Vector2 SetY(Vector2 v, float y)
        {
            Vector2 tmpV = v;
            tmpV.y = y;
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 设置 Vector3 的 x 值
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Vector3 SetX(Vector3 v, float x)
        {
            Vector3 tmpV = v;
            tmpV.x = x;
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 设置 Vector3 的 y 值
        /// </summary>
        /// <param name="v"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static Vector3 SetY(Vector3 v, float y)
        {
            Vector3 tmpV = v;
            tmpV.y = y;
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 设置 Vector3 的 z 值
        /// </summary>
        /// <param name="v"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public static Vector3 SetZ(Vector3 v, float z)
        {
            Vector3 tmpV = v;
            tmpV.z = z;
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 x 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="x"></param>
        public static void SetLocalScaleX(Transform transform, float x)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.x = x;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 y 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="y"></param>
        public static void SetLocalScaleY(Transform transform, float y)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.y = y;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 z 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="z"></param>
        public static void SetLocalScaleZ(Transform transform, float z)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.z = z;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 x 和 y 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public static void SetLocalScaleXY(Transform transform, float x, float y)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.y = y;
            tmpV.x = x;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 x 和 z 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="x"></param>
        /// <param name="z"></param>
        public static void SetLocalScaleXZ(Transform transform, float x, float z)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.z = z;
            tmpV.x = x;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 y 和 z 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        public static void SetLocalScaleYZ(Transform transform, float y, float z)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.z = z;
            tmpV.y = y;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 设置 Transform 局部大小的 x、y 和 z 值
        /// </summary>
        /// <param name="transform"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="z"></param>
        public static void SetLocalScale(Transform transform, float x, float y, float z)
        {
            Vector3 tmpV = transform.localScale;
            tmpV.x = x;
            tmpV.y = y;
            tmpV.z = z;
            transform.localScale = tmpV;
        }

        /// <summary>
        /// 限制 Vector2 的 x 值在 minX 和 maxX 之间
        /// </summary>
        /// <param name="v"></param>
        /// <param name="minX"></param>
        /// <param name="maxX"></param>
        /// <returns></returns>
        public static Vector2 ClampX(Vector2 v, float minX, float maxX)
        {
            Vector2 tmpV = v;
            tmpV.x = Mathf.Clamp(tmpV.x, minX, maxX);
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 限制 Vector2 的 y 值在 minY 和 maxY 之间
        /// </summary>
        /// <param name="v"></param>
        /// <param name="minY"></param>
        /// <param name="maxY"></param>
        /// <returns></returns>
        public static Vector2 ClampY(Vector2 v, float minY, float maxY)
        {
            Vector2 tmpV = v;
            tmpV.y = Mathf.Clamp(tmpV.y, minY, maxY);
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 限制 Vector3 的 x 值在 minX 和 maxX 之间
        /// </summary>
        /// <param name="v"></param>
        /// <param name="minX"></param>
        /// <param name="maxX"></param>
        /// <returns></returns>
        public static Vector3 ClampX(Vector3 v, float minX, float maxX)
        {
            Vector3 tmpV = v;
            tmpV.x = Mathf.Clamp(tmpV.x, minX, maxX);
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 限制 Vector3 的 y 值在 minY 和 maxY 之间
        /// </summary>
        /// <param name="v"></param>
        /// <param name="minY"></param>
        /// <param name="maxY"></param>
        /// <returns></returns>
        public static Vector3 ClampY(Vector3 v, float minY, float maxY)
        {
            Vector3 tmpV = v;
            tmpV.y = Mathf.Clamp(tmpV.y, minY, maxY);
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 限制 Vector3 的 z 值在 minZ 和 maxZ 之间
        /// </summary>
        /// <param name="v"></param>
        /// <param name="minZ"></param>
        /// <param name="maxZ"></param>
        /// <returns></returns>
        public static Vector3 ClampZ(Vector3 v, float minZ, float maxZ)
        {
            Vector3 tmpV = v;
            tmpV.z = Mathf.Clamp(tmpV.z, minZ, maxZ);
            v = tmpV;
            return v;
        }

        /// <summary>
        /// 获取两点中心位置
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static Vector2 Middle(Vector2 start, Vector2 end)
        {
            return (start + end) / 2f;
        }

        /// <summary>
        /// 获取两点中心位置
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static Vector3 Middle(Vector3 start, Vector3 end)
        {
            return (start + end) / 2f;
        }

        /// <summary>
        /// 获取 Vector2 的 Right 向量
        /// </summary>
        public static Vector3 Right(Vector2 direction)
        {
            return Right((Vector3)direction);
        }

        /// <summary>
        /// 获取 Vector3 的 Right 向量
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public static Vector3 Right(Vector3 direction)
        {
            Vector3 up = Vector3.up;
            if (Mathf.Approximately(direction.y, 1.0f) || Mathf.Approximately(direction.y, -1.0f))
            {
                up = Vector3.forward;
            }

            Vector3 right = Vector3.Cross(direction, up).normalized;
            return right;
        }

        /// <summary>
        /// 获取 Right 向量
        /// </summary>
        /// <param name="startPoint"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        public static Vector3 GetRightVector(Vector3 startPoint, Vector3 endPoint)
        {
            Vector3 direction = (endPoint - startPoint).normalized;
            Vector3 up = Vector3.up;
            if (Mathf.Approximately(direction.y, 1.0f) || Mathf.Approximately(direction.y, -1.0f))
            {
                up = Vector3.forward;
            }

            Vector3 right = Vector3.Cross(direction, up).normalized;
            return right;
        }

        /// <summary>
        /// 过点 point ，作 endPoint 和 startPoint 所形成直线的垂线，返回交点位置
        /// </summary>
        public static Vector3 GetIntersectionPoint(Vector3 startPoint, Vector3 endPoint, Vector3 point)
        {
            Vector3 direction = (endPoint - startPoint).normalized;
            Vector3 a = point - startPoint;
            Vector3 b = Vector3.Project(a, direction);
            Vector3 intersection = startPoint + b;
            return intersection;
        }

        /// <summary>
        /// Vector3 转 Vector2
        /// </summary>
        /// <param name="v3"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static Vector2 ToVector2(Vector3 v3, string format = "xz")
        {
            switch (format)
            {
                case "xz":
                    return new Vector2(v3.x, v3.z);
                case "xy":
                    return new Vector2(v3.x, v3.y);
            }

            return Vector2.zero;
        }

        /// <summary>
        /// Vector2 转 Vector3
        /// </summary>
        /// <param name="v2"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static Vector3 ToVector3(Vector2 v2, string format = "xz")
        {
            switch (format)
            {
                case "xz":
                    return new Vector3(v2.x, 0, v2.y);
                case "xy":
                    return new Vector3(v2.x, v2.y, 0);
            }

            return Vector3.zero;
        }

        /// <summary>
        /// 计算两条向量的角度
        /// </summary>
        public static float AngleBetween(Vector2 v, Vector2 other)
        {
            float angle = Vector2.Angle(v, other);
            angle *= Mathf.Sign(Vector3.Cross(v, other).z);
            return angle;
        }

        /// <summary>
        /// 旋转矩阵转为Forward向量
        /// </summary>
        /// <param name="q"></param>
        /// <returns></returns>
        public static Vector3 Forward(Quaternion q)
        {
            Vector3 euler = q.eulerAngles;
            float yaw = euler.y;
            float z = Mathf.Sin(yaw * Mathf.Deg2Rad);
            float x = Mathf.Cos(yaw * Mathf.Deg2Rad);
            return new Vector3(x, 0, z).normalized;
        }

        /// <summary>
        /// 克隆 Vector3
        /// </summary>
        /// <param name="v3"></param>
        /// <returns></returns>
        public static Vector3 Clone(Vector3 v3)
        {
            return new Vector3(v3.x, v3.y, v3.z);
        }

        /// <summary>
        /// 分段获取 Vector3 数组
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static Vector3[] SplitVector3s(Vector3 start, Vector3 end, int count)
        {
            Vector3[] splitRes = new Vector3[count];
            for (int i = 0; i < count; i++)
            {
                float scale = (float)i / count;
                Vector3 tmp = Vector3.Lerp(start, end, scale);
                splitRes[i] = tmp;
            }

            return splitRes;
        }

        /// <summary>
        /// 分段获取 Quaternion 数组
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static Quaternion[] SplitQuaternions(Quaternion start, Quaternion end, int count)
        {
            Quaternion[] splitRes = new Quaternion[count];
            for (int i = 0; i < count; i++)
            {
                float scale = (float)i / count;
                Quaternion tmp = Quaternion.Lerp(start, end, scale);
                splitRes[i] = tmp;
            }

            return splitRes;
        }

        /// <summary>
        /// 计算点到直线的距离
        /// </summary>
        /// <param name="v"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public static float DistanceFromPointToLine(Vector3 v, Vector3 start, Vector3 end)
        {
            Vector3 i = GetIntersectionPoint(start, end, v);
            return Vector3.Distance(v, i);
        }

        /// <summary>
        /// 判断该组点是否为顺时针
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static bool IsClockwise(List<Vector2> points)
        {
            float crossProduct = 0;

            for (int i = 1; i < points.Count; i++)
            {
                Vector3 vec1 = new Vector3(points[i - 1].x, points[i - 1].y, 0);
                Vector3 vec2 = new Vector3(points[i].x, points[i].y, 0);

                crossProduct += Vector3.Cross(vec1, vec2).z;
            }

            return crossProduct > 0;
        }

        /// <summary>
        /// 向量的四舍五入或截断
        /// </summary>
        /// <param name="vector3"></param>
        /// <param name="epsilon"></param>
        /// <returns></returns>
        public static Vector3 Round(Vector3 vector3, float epsilon = 0.1f)
        {
            // 在方法内部，对key参数进行四舍五入或截断，使它符合期望的精度
            vector3.x = Mathf.Round(vector3.x / epsilon) * epsilon;
            vector3.y = Mathf.Round(vector3.y / epsilon) * epsilon;
            vector3.z = Mathf.Round(vector3.z / epsilon) * epsilon;

            return vector3;
        }

        /// <summary>
        /// 插值到目标位置
        /// </summary>
        /// <param name="target"></param>
        /// <param name="targetPose"></param>
        /// <param name="callback"></param>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static IEnumerator LerpMoveTo(Transform target, Transform targetPose, Action callback = null, float speed = 2f)
        {
            while (true)
            {
                target.position = Vector3.Lerp(target.position, targetPose.position, speed * 0.016f);
                target.rotation = Quaternion.Lerp(target.rotation, targetPose.rotation, speed * 0.016f);
                if (Vector3.Distance(target.position, targetPose.position) < 0.01f && Quaternion.Angle(target.rotation, targetPose.rotation) < 0.01f)
                {
                    break;
                }

                yield return new WaitForSeconds(0.016f);
            }

            target.position = targetPose.position;
            target.rotation = targetPose.rotation;
            callback?.Invoke();
        }

        /// <summary>
        /// 判断该点是否在矩形范围内
        /// </summary>
        /// <param name="position"></param>
        /// <param name="center"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static bool IsInBoxRange(Vector3 position, Vector3 center, Vector3 size)
        {
            return position.x >= center.x - size.x / 2f && position.x <= center.x + size.x / 2f &&
                   position.z >= center.z - size.z / 2f && position.z <= center.z + size.z / 2f;
        }

        /// <summary>
        /// 单个轴方向上是否小于指定的距离
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        public static bool IsWithinDistanceOnAxes(Vector3 start, Vector3 end, float distance)
        {
            float x = Mathf.Abs(end.x - start.x);
            float y = Mathf.Abs(end.y - start.y);

            return x < distance && y < distance;
        }
    }
}
